package org.j4me.util;

import java.util.Vector;

import org.j4me.logging.Log;

// Simple Operations Queue
// It runs in an independent thread and executes Operations serially
public class OperationsQueue implements Runnable {
	private volatile boolean running = true;
	private final Vector operations = new Vector();
	private Thread _thread;
	static private OperationsQueue _instance = new OperationsQueue();

	public OperationsQueue() {
		// Notice that all operations will be done in another
		// thread to avoid deadlocks with GUI thread
		_thread = new Thread(this);
		_thread.start();
		
	}

	/**
	 * push operation
	 * @param nextOperation
	 */
	public static void push(Operation nextOperation) {
		if (_instance._thread == Thread.currentThread())
			_instance.runOperation(nextOperation);
		else
			_instance.enqueueOperation(nextOperation);
	}

	public static void runAndWait(Operation nextOperation) {
		if (_instance._thread == Thread.currentThread())
			_instance.runOperation(nextOperation);
		else {
			_instance.enqueueOperation(nextOperation);
			synchronized (nextOperation) {
				try {
					nextOperation.wait();
				} catch (InterruptedException e) {
				}
			}
		}
	}

	public void enqueueOperation(Operation nextOperation) {
		nextOperation.setWaiting(true);
		operations.addElement(nextOperation);
		synchronized (this) {
			notify();
		}
	}

	// stop the thread
	public void abort() {
		running = false;
		synchronized (this) {
			notify();
		}
	}

	public void run() {
		while (running) {
			while (operations.size() > 0) {
				runOperation(((Operation) operations.firstElement()));
				operations.removeElementAt(0);
			}
			synchronized (this) {
				try {
					wait();
				} catch (InterruptedException e) {
					// it doesn't matter
				}
			}
		}
	}

	private void runOperation(Operation ope) {
		try {
			ope.setWaiting(false);
			ope.setExecuting(true);
			if (!ope.isDeleted())
				ope.execute();
		} catch (Throwable e) {
			// Nothing to do. It is expected that each operations handle
			// their own locally exception but this block is to ensure
			// that the queue continues to operate
			// do not debug (risk of deadlock);
			Log.error("RunOperation [" + e.getMessage() +"]",e);
		}
		synchronized (ope) {
			if (ope != null)
				ope.setExecuting(false);
			ope.notify();
		}
	}

	public static Vector getPendingOperations() {
		return _instance.operations;
	}
}
